//------------------------------------------------------------------------------
// The confidential and proprietary information contained in this file may
// only be used by a person authorised under and to the extent permitted
// by a subsisting licensing agreement from ARM Limited.
//
//            (C) COPYRIGHT 2012 ARM Limited.
//                ALL RIGHTS RESERVED
//
// This entire notice must be reproduced on all copies of this file
// and copies of this file may only be made by a person if such person is
// permitted to do so under the terms of a subsisting license agreement
// from ARM Limited.
//------------------------------------------------------------------------------
//  Version and Release Control Information:
//
//  File Revision       : 129771
//
//  Date                :  2012-05-11 11:13:49 +0100 (Fri, 11 May 2012)
//
//  Release Information : PL401-r0p1-00eac0
//
//------------------------------------------------------------------------------
//  File Purpose        : A Bus matrix write channel router. Stalls connected
//                        slave inetrface write channels until the address
//                        is arbitrated.
//   
//  Key Configuration Details-
//      - Number of connected slave interfaces 2
//      - Acceptance capability 4
//
// Notes on port naming conventions- 
//
//     All AXI point to point connections can be considered a 
//     MasterInterface - SlaveInterface connection. 
//
//     The AXI ports on the NIC400 A3BM are named as follows-  
//
//     *_m<n> suffix to denote a MasterInterface (connect to external AXI slave)
//     *_s0 suffix to denote the SlaveInterface  (connect to external AXI master) 
//
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Module Declaration
//------------------------------------------------------------------------------

module nic400_switch2_wr_sel_ml3_ysyx_rv32
  (
    // Miscelaneous connections
    aclk,
    aresetn,
    // Channel Enable
    aw_sel,
    awready_m,
    // SlaveInterface 0
    // Write Channel
    wdata_s0,
    wstrb_s0,   
    wlast_s0,
    wvalid_s0,
    wready_s0,

    // SlaveInterface 1
    // Write Channel
    wdata_s1,
    wstrb_s1,   
    wlast_s1,
    wvalid_s1,
    wready_s1,

    // MasterInterface 
    // Write Channel
    wdata_m,
    wstrb_m,
    wlast_m,
    wvalid_m,
    wready_m
  );

  // ---------------------------------------------------------------------------
  //  parameters
  // ---------------------------------------------------------------------------


  // ---------------------------------------------------------------------------
  //  Port definitions
  // ---------------------------------------------------------------------------

  // MasterInterface 
  // Write Channel
  output  [31:0]    wdata_m;
  output  [3:0]     wstrb_m;
  output            wlast_m;
  output            wvalid_m;
  input             wready_m;
  // SlaveInterfaces
  // Write Channel
  input  [31:0]     wdata_s0;
  input  [3:0]      wstrb_s0;   
  input             wlast_s0;
  input             wvalid_s0;
  output            wready_s0;
  // Write Channel
  input  [31:0]     wdata_s1;
  input  [3:0]      wstrb_s1;   
  input             wlast_s1;
  input             wvalid_s1;
  output            wready_s1;


  // Channel Enable
  input [1:0]    aw_sel;
  input             awready_m;

  // Miscelaneous connections
  input             aclk;
  input             aresetn;

  //------------------------------------------------------------------------------
  // Wires 
  //------------------------------------------------------------------------------

  wire              add_push;  // 
  wire              last_beat; // Last beat of write burst status flag
  // Masked Write Channel
  wire   [31:0]     wdata_masked0;
  wire   [3:0]      wstrb_masked0;   
  wire              wlast_masked0;
  wire              wvalid_masked0;
  // Masked Write Channel
  wire   [31:0]     wdata_masked1;
  wire   [3:0]      wstrb_masked1;   
  wire              wlast_masked1;
  wire              wvalid_masked1;


  wire   [1:0]     next_wr_ch_en; // next one hot vector of wr channel enables 
 
  wire              wt_push; // Push value onto fifo
  wire              wt_pop; // Pop value from fifo
  wire              next_enc; // Encoded slave interface select 
  wire              enc_fifo_op; // Encoded slave interface select
  reg    [1:0]      fifo_op; // Decoded one hot select value from the fifo
  reg               next_wr_ptr; // Next value for fifo write pointer
  reg               next_rd_ptr; // Next value for fifo read pointer
  reg          wr_ptr_en; // One hot vector to enable selected write 
                                    // channel
  wire              next_empty; // Status of fifo


  //------------------------------------------------------------------------------
  // Registers 
  //------------------------------------------------------------------------------

  reg               aw_sel_reg; // 
  reg               awready_reg;  // 
  reg   [1:0]       wr_ch_en; // One hot vector, of write channel enables
  reg               fifo; // FIFO array for storing active transactions
  reg               wr_ptr; // fifo write pointer
  reg               rd_ptr; // fifo read pointer  
  reg               empty; // Status of fifo

  // ---------------------------------------------------------------------------
  //  start of code
  // ---------------------------------------------------------------------------


   //----------------------- Write address push detection ----------------------

   // Register incoming add select aw_sel and awready_m from master i/f to
   // enable detection of a new address push
   always @(posedge aclk or negedge aresetn)
     begin : p_add_push_seq
       if (!aresetn)
         begin
             aw_sel_reg  <= 1'b0;
             awready_reg <= 1'b0;
         end
       else
         begin
            if ((|aw_sel) || aw_sel_reg)
             begin
                 aw_sel_reg  <= |aw_sel;
             end
            if (awready_m || awready_reg)
             begin
                awready_reg   <= awready_m;
             end
         end
     end // p_add_push_seq

   // Detect new address push ensuring no dependency on the awready_m 
   // completion of the address transaction
   assign add_push = (|aw_sel & ~aw_sel_reg) | (|aw_sel & aw_sel_reg & awready_reg);


   // Determine when current transaction is ending
   assign last_beat = wvalid_m & wready_m & wlast_m;




   //------------------------- Write Source Enable FIFO -------------------------

   // Create a push to the FIFO when there is a selected one-hot input value 
   // and there won't be a one hot value next cycle stored in the enable reg 
   // with the FIFO also being empty. 
   // No check for fifo full as this will be prevented by the add channel arb
   assign wt_push =  (add_push && |wr_ch_en && !empty)
               || (add_push && |wr_ch_en && !last_beat);
   // Pop from the fifo when the last beat of the current transaction is 
   // ending and the fifo is not empty
   assign wt_pop = (|wr_ch_en) && last_beat && !empty;

   assign next_enc = aw_sel[1];


   // Determine next fifo pointers depending on write trans fifo push and pop
   always @(wr_ptr or rd_ptr or wt_push or wt_pop)
     begin : p_fifo_comb
       next_wr_ptr = wr_ptr;
       next_rd_ptr = rd_ptr;
       if (wt_push)
         begin
             if (wr_ptr == 1'b0)
               next_wr_ptr = 1'b1;
             else
               next_wr_ptr = 1'b0;
         end
       if (wt_pop)
         begin
             if (rd_ptr == 1'b0)
               next_rd_ptr = 1'b1;
             else
               next_rd_ptr = 1'b0;
         end
     end // p_fifo_comb

   // Generate a one hot vector to select next write fifo slot
   always @(wt_push)
     begin : p_ptr_en

                wr_ptr_en = wt_push;
     end // p_ptr_en

   // Manage fifo pointers
   always @(posedge aclk or negedge aresetn)
     begin : p_fifo_ptr_seq
       if (!aresetn) begin
              wr_ptr <= 1'b0;
              rd_ptr <= 1'b0;
        end else begin
           if (wt_push) 
                 wr_ptr <= next_wr_ptr;
           if (wt_pop) 
               rd_ptr <= next_rd_ptr;
        end
     end // p_fifo_ptr_seq

   // fifo registers 
   // Note these registers are datapath only and therefore not reset to reduce
   // area and power
   always @(posedge aclk)
     begin : p_fifo_seq
         if (wr_ptr_en) begin
                 fifo <= next_enc;
         end
     end // p_fifo_seq

   // Select current head of FIFO
   assign enc_fifo_op = fifo;
   // Create one hot enable vector from encoded fifo output
   always @(enc_fifo_op)
     begin : p_uncode_comb
       fifo_op = {2{1'b0}};
       fifo_op[enc_fifo_op] = 1'b1;
     end // p_uncode_comb

   // Determine next empty flag state
   assign next_empty = wt_push && !wt_pop ? 1'b0
                         : !wt_push && wt_pop ? 1'b1
                         :  empty;


   always @(posedge aclk or negedge aresetn)
     begin : p_empty_seq
       if (!aresetn)
         begin
             empty <= 1'b1;
         end
       else if (wt_push || wt_pop)
         begin
             empty <= next_empty;
         end
    end // p_empty_seq

   //----------------------- Write Source Enable Selection ----------------------

   // Determine next slave interface input write channel to enable or clear
   // If the fifo isn't empty then assign to the fifo output otherwise assign 
   // the incoming selected slave interface
   assign next_wr_ch_en = ~empty ? fifo_op : (add_push) ? aw_sel : {2{1'b0}};



   always @(posedge aclk or negedge aresetn)
     begin : p_wr_ch_en_seq
       if (!aresetn)
         begin
             wr_ch_en    <= {2{1'b0}};
         end
       else if (last_beat | (~|wr_ch_en & ((add_push) | ~empty)))
         begin
             wr_ch_en    <=   next_wr_ch_en;
         end
     end // p_wr_ch_en_seq

     

   //------------------------ Write Channel Multiplexing -----------------------

    assign wdata_masked0  = wdata_s0 & {32{wr_ch_en[0]}};
    assign wstrb_masked0  = wstrb_s0 & {4{wr_ch_en[0]}}; 
    assign wlast_masked0  = wlast_s0 & wr_ch_en[0];
    assign wvalid_masked0 = wvalid_s0 & wr_ch_en[0];

    assign wdata_masked1  = wdata_s1 & {32{wr_ch_en[1]}};
    assign wstrb_masked1  = wstrb_s1 & {4{wr_ch_en[1]}}; 
    assign wlast_masked1  = wlast_s1 & wr_ch_en[1];
    assign wvalid_masked1 = wvalid_s1 & wr_ch_en[1];

    // Master Interface Write Channel Assignment
    assign wdata_m  = wdata_masked0 | wdata_masked1;
    assign wstrb_m  = wstrb_masked0 | wstrb_masked1;
    assign wlast_m  = wlast_masked0 | wlast_masked1;
    assign wvalid_m = wvalid_masked0 | wvalid_masked1;

    // Mask the ready signals back to the current slave interface
    assign wready_s0 = (wready_m & wr_ch_en[0]);
    assign wready_s1 = (wready_m & wr_ch_en[1]);

    //------------------------------------------------------------------------------
    // OVL Assertions
    //------------------------------------------------------------------------------
    // synopsys translate_off
 
    `ifdef ARM_ASSERT_ON

      parameter  WR_ISS_DEPTH = 2;
      assert_zero_one_hot #(0,2,0,"ERROR, More than one write channel enabled")
         ovl_write_channel_en
           (
            .clk       (aclk),
            .reset_n   (aresetn),
            .test_expr (wr_ch_en)
            );

       assert_fifo_index #(0, WR_ISS_DEPTH - 1, 1, 1, 0,"ERROR, Write tracker fifo under or overflowed")
         ovl_wr_tracker_fifo_index
           (
            .clk       (aclk),
            .reset_n   (aresetn),
            .push      (wt_push),
            .pop       (wt_pop)
            );

       assert_implication #(0,0,"ERROR, Write tracker empty flag does not have the expected value")
         ovl_wr_tracker_empty_flag
          (
           .clk       (aclk),
           .reset_n   (aresetn),
           .antecedent_expr (empty),
           .consequent_expr (rd_ptr == wr_ptr)
         );
 
       assert_never #(1,0,"ERROR, Write channel select is X")
         ovl_wr_ch_sel_x
          (
           .clk       (aclk),
           .reset_n   (aresetn),
           .test_expr (!empty && |(enc_fifo_op ^ enc_fifo_op))
         );
 
       assert_never #(1,0,"ERROR, wr_sel fifo_op index to large")
         ovl_fifo_op_index
          (
           .clk       (aclk),
           .reset_n   (aresetn),
           .test_expr (!empty && (enc_fifo_op >= 2))
         );


    `endif
    // synopsys translate_on


endmodule

//  --=============================== End ====================================--

